/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package unc.pds.login;

import java.io.IOException;
import java.rmi.RemoteException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import javax.ejb.FinderException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import javax.sql.DataSource;
import unc.pds.data.Consts;
import unc.pds.model.UserRemote;
import unc.pds.model.UserRemoteHome;

/**
 *
 * @author ain
 */
public class PDSLoginModule implements LoginModule {

    private Subject subject;
    private CallbackHandler handler;
    private Map sharedState;
    private Map option;
    private boolean userAuthenticated;
    private String username;
    private Boolean debug;
    private Object caching;

    public void initialize(Subject subject,
            CallbackHandler callbackHandler, Map sharedState, Map options) {
        this.subject = subject;
        this.handler = callbackHandler;
        this.sharedState = sharedState;
        this.option = options;
        this.debug = Boolean.valueOf((String) options.get("debug"));
        this.caching = options.get("caching");
        if (Consts.DEBUG_MODE) System.out.println("PDSLoginModule initialized");
    }

    private Connection getConnection() throws NamingException, SQLException {
        DataSource ds = (DataSource) (new InitialContext()).lookup("oracle/SOS");
        return ds.getConnection();
    }

    private Long getObjectTypeID(String name) throws RemoteException {
        {
            try {
                Connection conn = getConnection();
                ResultSet ans = null;
                Statement query = null;
                try {
                    query = conn.createStatement();
                    ans = query.executeQuery("SELECT * FROM OBJECT_TYPES "
                            + "WHERE NAME='" + name + "'");
                    if (ans.next()) {
                        return new Long(ans.getLong(Consts.OBJECT_TYPES__ID));
                    } else {
                        throw new RemoteException("SecB.getObjTypeID: not such object_type " + name);
                    }
                } finally {
                    ans.close();
                    query.close();
                    conn.close();
                }
            } catch (Exception e) {
                throw new RemoteException("SecB.getObjTypeID: " + e.getLocalizedMessage());
            }
        }
    }

    private Long getAttributeID(String name, Long object_type_id) throws FinderException, RemoteException {

        try {
            Connection conn = getConnection();
            ResultSet ans = null;
            Statement query = null;
            try {
                query = conn.createStatement();
                ans = query.executeQuery("SELECT * FROM ATTRIBUTES "
                        + "WHERE OBJECT_TYPE_ID='" + object_type_id + "' AND "
                        + "NAME='" + name + "'");
                if (ans.next()) {
                    return new Long(ans.getLong(Consts.ATTRIBUTES__ATTRIBUTE_ID));
                } else {
                    throw new FinderException("SecB.getAttrID: not attr_id for " + name);
                }
            } finally {
                ans.close();
                query.close();
                conn.close();
            }
        } catch (Exception e) {
            throw new RemoteException("SecB.getAttr_id: " + e.toString());
        }
    }

    private Long doLogin(String name, String pass) {
        try {
            Long object_type_id = getObjectTypeID(Consts.OBJECT_TYPE__USER);
            Long pass_attr_id = getAttributeID(Consts.ATTR__USER_PASSWORD, object_type_id);
            Long nick_attr_id = getAttributeID(Consts.ATTR__USER_NICKNAME, object_type_id);
            Connection conn = getConnection();
            ResultSet ans = null;
            Statement query = null;
            try {
                query = conn.createStatement();
                String sqlquerry = "SELECT * FROM PARAMS WHERE "
                        + Consts.PARAMS__ATTRIBUTE_ID + "=" + pass_attr_id + " AND "
                        + Consts.PARAMS__VALUE + "='" + pass + "' AND "
                        + Consts.PARAMS__OBJECT_ID + " IN ("
                        + "SELECT " + Consts.PARAMS__OBJECT_ID + " FROM PARAMS WHERE "
                        + Consts.PARAMS__ATTRIBUTE_ID + "=" + nick_attr_id + " AND "
                        + Consts.PARAMS__VALUE + "='" + name + "')";
                ans = query.executeQuery(sqlquerry);
                if (ans.next()) {
                    return new Long(ans.getLong(Consts.PARAMS__OBJECT_ID));
                    //return sessionKey;
                } else {
                    return null;
                }
            } finally {
                ans.close();
                query.close();
                conn.close();
            }
        } catch (Exception e) {
            if (Consts.DEBUG_MODE) System.out.println("ERROR: Login: " + e.getLocalizedMessage());
            return null;
        }
    }

    public boolean login() throws LoginException {

        NameCallback name = new NameCallback("Username:");
        PasswordCallback password = new PasswordCallback("Password", false);
        try {
            handler.handle(new Callback[]{name, password});
        } catch (IOException e) {
            LoginException ex = new LoginException("IO error getting credentials: " + e.getMessage());
            e.initCause(e);
            throw ex;
        } catch (UnsupportedCallbackException e) {
            LoginException ex = new LoginException("UnsupportedCallback: " + e.getMessage());
            e.initCause(e);
            throw ex;
        }

        String usernameText = name.getName();
        String passwordText = String.valueOf(password.getPassword());

        try {
            userAuthenticated = (doLogin(usernameText, passwordText) != null);
        } catch (Exception e) {
            throw new FailedLoginException("PDSLoginModule.login: " + e.getLocalizedMessage());
        }

        if (userAuthenticated) {
            username = usernameText;
            UsernameCredential cred = new UsernameCredential(username);
            subject.getPublicCredentials().add(cred);
            return true;
        } else {
            throw new FailedLoginException("Username/Password for "
                    + usernameText + " incorrect.");
        }
    }

    public boolean commit() throws LoginException {
        if (userAuthenticated) {
            subject.getPrincipals().add(new UserPrincipal(username));
        }
        username = null;
        return true;
    }

    public boolean abort() {
        username = null;
        return true;
    }

    public boolean logout() {
        username = null;
        return true;
    }

    public Subject getSubject() {
        return subject;
    }

    public UserRemote getUser() {
        if (userAuthenticated) {
            try {
                UserRemote user =
                        ((UserRemoteHome) (new InitialContext()).lookup("ejb/User")).create(username, null);
                return user;
            } catch (Exception e) {
                if (Consts.DEBUG_MODE) System.out.println("" + e.getLocalizedMessage());
            }
        }
        return null;
    }
}
